<!--
 * @Author: your name
 * @Date: 2021-10-26 20:58:05
 * @LastEditTime: 2021-10-26 21:18:52
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \bigdate\src\components\verifyComp\index.vue
-->
<template>
    <div class="drag_content">
        <div
            ref="line"
            class="drag_content-line"
            @mousedown="onMousedown"
            @touchstart="onMousedown"
        >
            <div class="drag_content-line-test">{{ text }}</div>
            <div ref="progress" class="drag_content-line-progress">
                <div class="protext">{{ state.tiptext }}</div>
            </div>
            <div
                ref="Ebar"
                class="drag_content-line-bar"
                @mousemove="onMousemove"
                @mouseleave="onMouseleave"
                @mouseup="onMouseleave"
                @touchend.prevent.stop="onMouseleave"
                @touchmove.prevent.stop="onMousemove"
            >
                <svg-icon v-if="state.finsh" icon-class="bug" size="1.2"></svg-icon>
                <svg-icon v-else icon-class="home" size="1.2"></svg-icon>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent, ref, reactive } from 'vue'

type EventType = Event & MouseEvent & TouchEvent;
interface stateType {
  moveX: number;
  startTime: number;
  endTime: number;
  tiptext: String;
  finsh: Boolean;
}
const props = {
    text: {
        type: String,
        default: () => '拖动进行校验',
    },
} as const
export default defineComponent({
    name: 'DragBar',
    props,
    emits: {
        success: null,
    },
    setup(_, { emit }) {
        let Ebar = ref<HTMLElement | null>(null)
        let line = ref<HTMLElement | null>(null)
        let progress = ref<HTMLElement | null>(null)
        let isMove = ref<Boolean>(false)
        let pagesX = ref()
        let state = reactive({
            moveX: 0,
            startTime: 0,
            endTime: 0,
            tiptext: '向右滑动',
            finsh: false,
        }) as stateType
        // 整体思路：就是将鼠标点击时刻的坐标记录为p，通过移动坐标减去p，两者之间的差值就是需要移动的距离，因为首尾位置会多出自身大小，所以需要减去自身宽度
        // 鼠标按下
        const onMousedown = (e: EventType) => {
            let w = Ebar.value?.getBoundingClientRect().width
            // 点击获取初始偏移位置 也就是获取到最左侧的距离，获取的是左上角到最左侧 的距离
            // 以下这种写法兼容移动端Touche事件
            let lefts = e.clientX ?? e.targetTouches[0].pageX
            // 这种兼容滚动条 不用考虑横向滚动条问题
            pagesX.value = lefts - w
            state.startTime = new Date().getTime()
            isMove.value = true
        }
        // 鼠标离开
        const onMouseleave = (e: EventType) => {
            isMove.value = false
            let w = Ebar.value?.getBoundingClientRect().width
            let lw = line.value?.getBoundingClientRect().width
            if (state.moveX < lw) {
                addTransition()
                onChangeSizeAndLeft(w, w)
            }
        }
        // 鼠标移动
        const onMousemove = (e: EventType) => {
            // 判断现在状态是否可以移动 && 结束状态
            e.preventDefault()
            if (isMove.value && !state.finsh) {
                let w: Number = Ebar.value?.getBoundingClientRect().width
                let lw: number = line.value?.getBoundingClientRect().width
                let cX = e.clientX ?? e.targetTouches[0].pageX
                // 公式:偏移量 = 鼠标移动的距离-当前元素到最左测的距离
                // 整个silder有限距离 =  偏移量-自身宽度 ：减去自身的目的就是滑块的距离是以左上角，所以真正到头是右边到头需要减去自身
                // 比较通俗易懂的就是 cX - pagesX.value - w  很直观的得出移动距离，但是需要改变下面的配置
                state.moveX = Math.floor(cX - pagesX.value)
                // 当小于自身宽度的时候不进行赋值
                if (state.moveX < w) return
                if (state.moveX >= 0 && state.moveX < lw) {
                    onChangeSizeAndLeft(state.moveX, w)
                } else if (state.moveX >= lw) {
                    Object.assign(progress.value?.style ?? {}, {
                        background: '#1eb173',
                    })
                    Object.assign(line.value?.style ?? {}, {
                        borderColor: '#1ac47d',
                    })
                    state.finsh = true
                    state.endTime = new Date().getTime()
                    let limit = state.endTime - state.startTime
                    let times = (limit / 1000).toFixed(1)
                    state.tiptext = `验证成功！${times}s`
                    // 4 为两边的border宽度
                    onChangeSizeAndLeft(lw - 4, w)
                    onSuccess(times)
                }
            }
            return false
        }
        // 改变 进度条尺寸和滑块位置
        const onChangeSizeAndLeft = (left, w) => {
            Object.assign(Ebar.value?.style ?? {}, {
                left: `${left - w}px`,
            })
            Object.assign(progress.value?.style ?? {}, {
                width: `${left}px`,
            })
        }
        // 添加往返动画
        const addTransition = () => {
            Object.assign(Ebar.value?.style ?? {}, {
                transition: 'all .2s ease 0s',
            })
            Object.assign(progress.value?.style ?? {}, {
                transition: 'all .2s ease 0s',
            })
            setTimeout(() => {
                Object.assign(Ebar.value?.style ?? {}, {
                    transition: 'none',
                })
                Object.assign(progress.value?.style ?? {}, {
                    transition: 'none',
                })
            }, 250)
        }
        // 重置
        const reload = () => {
            if (!state.finsh) return false
            let w: Number = Ebar.value?.getBoundingClientRect().width
            state.finsh = false
            isMove.value = false
            state.tiptext = '向右滑动'
            init(w)
        }

        // 成功状态
        const onSuccess = (times) => {
            // 返回参数 状态+滑动时间
            emit('success', { statu: 1, time: times })
        }
        // 初始化样式+状态
        const init = (w) => {
            Object.assign(progress.value?.style ?? {}, {
                background: 'rgb(51, 99, 153)',
            })
            Object.assign(line.value?.style ?? {}, {
                borderColor: 'rgb(83, 128, 252)',
            })
            addTransition()
            onChangeSizeAndLeft(w, w)
        }
        document.addEventListener('mouseup', onMouseleave)
        // document.addEventListener("mouseup", onMouseleave);
        return {
            onMousedown,
            onMousemove,
            onMouseleave,
            Ebar,
            line,
            progress,
            state,
            reload,
        }
    },
})
</script>

<style lang="less" scoped>
@heights: 40px;
.drag_content {
  width: 100%;

  &-line {
    height: @heights;
    background: #ccc;
    position: relative;
    border: 2px solid rgb(83, 128, 252);
    box-sizing: content-box;
    border-radius: 5px;
    overflow: hidden;
    transition: all 0.2s ease 0.1s;
    &-test {
      width: 100%;
      height: 40px;
      text-align: center;
      position: absolute;
      top: 0;
      left: 0;
      line-height: 40px;
    }
    &-bar {
      position: absolute;
      top: 0;
      left: 0;
      width: @heights;
      height: @heights;
      background: rgb(246, 244, 247);
      cursor: move;
      //   box-shadow: 0 2px 5px 0 rgba(69, 214, 81, 0.5);
      border-radius: 3px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    &-progress {
      width: 0;
      height: 40px;
      background: rgb(51, 99, 153);
      overflow: hidden;
      border-radius: 3px;
      position: absolute;
      top: 0;
      left: 0;
      .protext {
        height: 100%;
        width: 100%;
        // text-align: center;
        padding-left: 20px;
        line-height: @heights;
        font-weight: bold;
        color: white;
        font-size: 13px;
      }
    }
  }
}
</style>
